package map;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;

/**
 * Created by 佳星 on 08-08-2014.
 */
public class TouchImage extends ImageView {

    //图片操作矩阵
    private Matrix matrix = new Matrix();
    //惯性
    private static final float FRICTION = 0.9f;
    //操作模式
    private static final int NONE = 0;
    private static final int ZOOM = 1;
    private static final int DRAG = 2;

    private int mode = NONE;
    //距离参数
    private float xSpace;
    private float ySpace;

    private float right;
    private float bottom;

    private float bmWidth;
    private float bmHeight;

    private float realWidth;
    private float realHeight;

    private float viewHeight;
    private float viewWidth;

    private float absoluteY;
    private float absoluteX;

    private float oldDis = 0f;
    //触碰点
    private PointF start = new PointF();
    private PointF last = new PointF();
    private PointF pointDist = new PointF();

    private float[] matrixValues = new float[9];
    //Scale参数
    private float saveScale = 1f;
    private float minScale = 1f;
    private float maxScale = 2f;
    //拖拽速度
    private float v = 0;
    private static final float maxV = 1f;
    private long lastDragTime = 0;

    private Context mComtext;
    private ScaleGestureDetector mScaleDetector;

    public TouchImage(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    public TouchImage(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public TouchImage(Context context) {
        super(context);
        init(context);
    }



    private OnTouchListener onTouchListener = new OnTouchListener() {
        private long dragTime;
        private float dragV;
        @Override
        public boolean onTouch(View view, MotionEvent event) {

            mScaleDetector.onTouchEvent(event);

            fillAbsoluteOffset();

            float currX = event.getX();
            float currY = event.getY();

            onViewClick(currX, currY);

            PointF curr = new PointF(currX,currY);
            switch (event.getAction() & MotionEvent.ACTION_MASK){
                case MotionEvent.ACTION_DOWN:
                    mode = DRAG;
                    last.set(currX,currY);
                    start.set(last);
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    oldDis = spacing(event);
                    if (oldDis > 10f){
//                    mid = midPoint(event);
                        mode = ZOOM;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (mode == DRAG){
                        v = dragV;
                    }
                    mode = NONE;
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    v = 0;
                    oldDis = spacing(event);
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (mode == DRAG){
                        dragV = spacing(last,curr) / (dragTime - lastDragTime) * FRICTION;
                        dragTime = System.currentTimeMillis();
                        dragV = Math.min(dragV,maxV);
                        lastDragTime =dragTime;
                        float distX = currX - last.x;
                        float distY = currY - last.y;
                        checkAndsetTranslate(distX, distY);
                        pointDist.set(distX, distY);
                        last.set(currX,currY);
                    }
                    break;
            }
            setImageMatrix(matrix);
            invalidate();
            return false;
        }
    };

    private void init(Context context){
        setClickable(true);
        mComtext = context;
        setScaleType(ScaleType.MATRIX);
        setImageMatrix(matrix);
        mScaleDetector = new ScaleGestureDetector(mComtext, new ScaleListener());
        setOnTouchListener(onTouchListener);

        matrix.setScale(saveScale ,saveScale);
    }

    private void initSize(){
        xSpace = viewWidth - (saveScale * bmWidth);
        ySpace = viewHeight - (saveScale * bmHeight);

        xSpace /= 2f;
        ySpace /= 2f;

        realWidth  = viewWidth  - 2 * xSpace;
        realHeight = viewHeight - 2 * ySpace;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        scrolling();
    }

    /**
     * 惯性滚动
     */
    private void scrolling (){//get
        final float deltaX = pointDist.x * v;
        final float deltaY = pointDist.y * v;
        if (deltaX > viewWidth || deltaY > viewHeight) {
            return;
        }
        v *= FRICTION;
        if (Math.abs(deltaX) < 0.1 && Math.abs(deltaY) < 0.1) {
            return;
        }
        moveBy(deltaX, deltaY);
    }
    public void moveBy (float deltaX, float deltaY){
        checkAndsetTranslate(deltaX, deltaY);
        setImageMatrix(matrix);
    }
    /**
     * 获取view 图片绝对偏移
     */
    void fillAbsoluteOffset(){
        matrix.getValues(matrixValues);
        absoluteX = matrixValues[Matrix.MTRANS_X];
        absoluteY = matrixValues[Matrix.MTRANS_Y];
    }
    public PointF getAbsoluteOffset(){
        fillAbsoluteOffset();
        return new PointF(absoluteX, absoluteY);
    }

    public PointF getAbsoluteCenter (){
        fillAbsoluteOffset();
        return new PointF( Math.abs(absoluteX) + viewWidth/2,Math.abs(absoluteY) + viewHeight/2 );
    }

    private void calcPadding(){
        right = viewWidth * saveScale - viewWidth - (2 * xSpace * saveScale);
        bottom= viewHeight * saveScale - viewHeight -(2 * ySpace * saveScale);
    }

    /**
     * 计算多点触控时两指间距
     * @param event
     * @return
     */
    float spacing(MotionEvent event){
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);

        return (float)Math.sqrt(x*x + y*y);
    }

    /**
     * 计算两个PointF间距
     * @param left
     * @param right
     * @return
     */
    float spacing(PointF left, PointF right){
        float x = left.x - right.x;
        float y = left.x - right.y;

        return (float)Math.sqrt(x*x + y*y);
    }

    /**
     * 多点触控时中点
     * @param event
     * @return
     */
    private PointF midPoint(MotionEvent event){
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        return new PointF(x/2, y/2);
    }

    /**
     * 检查边缘
     * @param distX
     * @param distY
     */
    void checkAndsetTranslate(float distX, float distY){
        float scaleWidth = Math.round(realWidth * saveScale);
        float scaleHeight = Math.round(realHeight * saveScale);
        fillAbsoluteOffset();
        final float x = absoluteX;
        final float y = absoluteY;
        if (scaleWidth < viewWidth) {
            distX = 0;
            if (y + distY > 0)
                distY = -y;
            else if (y + distY < -bottom)
                distY = -(y + bottom);
        } else if (scaleHeight < viewHeight) {
            distY = 0;
            if (x + distX > 0)
                distX = -x;
            else if (x + distX < -right)
                distX = -(x + right);
        }
        else {
            if (x + distX > 0)
                distX = -x;
            else if (x + distX < -right)
                distX = -(x + right);

            if (y + distY > 0)
                distY = -absoluteY;
            else if (y + distY < -bottom)
                distY = -(y + bottom);
        }
        postTranslate(distX, distY);
    }

    /**
     * Matrix.postTranslate
     * @param distX
     * @param distY
     */
    void postTranslate(float distX,float distY){
        matrix.postTranslate(distX, distY);
        fillAbsoluteOffset();
    }

    /**
     * [postScale description]
     * @param scaleFactor  [description]
     * @param scaleCenterX [description]
     * @param scaleCenterY [description]
     */
    protected void postScale(float scaleFactor, float scaleCenterX, float scaleCenterY){//get
        matrix.postScale(scaleFactor, scaleFactor, scaleCenterX, scaleCenterY);
        fillAbsoluteOffset();
    }

    public float getScale(){
        return saveScale;
    }

    public void setBitmap(Bitmap bm){
        bmWidth  = bm.getWidth();
        bmHeight = bm.getHeight();
        super.setImageBitmap(bm);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        float newWidth = MeasureSpec.getSize(widthMeasureSpec);
        float newHeight = MeasureSpec.getSize(heightMeasureSpec);

        viewWidth  = newWidth;
        viewHeight = newHeight;

        initSize();
        calcPadding();
    }
    private void postScaleToImage(float scaleFactor,float scaleFocusX,float scaleFocusY){
        float origScale = saveScale;
        saveScale *= scaleFactor;
        if (saveScale > maxScale) {
            saveScale = maxScale;
            scaleFactor = maxScale / origScale;
        } else if (saveScale < minScale) {
            saveScale = minScale;
            scaleFactor = minScale / origScale;
        }
        right = viewWidth * saveScale - viewWidth - (2 * xSpace * saveScale);
        bottom = viewHeight * saveScale - viewHeight - (2 * ySpace * saveScale);
//        0Log.v("right&bottom", String.format("right:%f  bottom:%f", right, bottom));

        // 显示的图片比源图片要小时进行缩放
        if (realWidth * saveScale <= viewWidth || realHeight * saveScale <= viewHeight) {
            final float scaleCenterX = viewWidth / 2;
            final float scaleCenterY = viewHeight / 2;
            postScale(scaleFactor, scaleCenterX, scaleCenterY);

            //在边缘时缩小图片的平移修正
            if (scaleFactor < 1) {
                fillAbsoluteOffset();
                final float x = absoluteX;
                final float y = absoluteY;
                if (scaleFactor < 1) {
                    if (Math.round(realWidth * saveScale) < viewWidth) {
                        float deltaX = 0,deltaY = 0;
                        if (y < -bottom){
                            deltaY = -(y + bottom);
                            postTranslate(deltaX, deltaY);
                        }
                        else if (y > 0){
                            deltaY = -y;
                            postTranslate(deltaX, deltaY);
                        }
                    } else {
                        float deltaX = 0,deltaY = 0;
                        if (x < -right){
                            deltaX = -(x + right);
                            postTranslate(deltaX, deltaY);
                        }
                        else if (x > 0){
                            deltaX = -x;
                            postTranslate(deltaX, deltaY);
                        }
                    }
                }
            }
        } else {
            // 图片已经被放大，以触摸点为中心，缩放图片。
            postScale(scaleFactor, scaleFocusX, scaleFocusY);
            fillAbsoluteOffset();
            final float x = absoluteX;
            final float y = absoluteY;

            // 缩小图片时，如果在图片边缘缩小，为避免边缘出现空白，对图片进行平移处理
            if (scaleFactor < 1) {
                float deltaX = 0,deltaY = 0;
                if (x < -right){
                    deltaX = -(x + right);
                    deltaY = 0;
                    postTranslate(deltaX, deltaY);
                }
                else if (x > 0){
                    deltaX = -x;
                    deltaY = 0;
                    postTranslate(deltaX, deltaY);
                }
                if (y < -bottom){
                    deltaX = 0;
                    deltaY = -(y + bottom);
                    postTranslate(deltaX, deltaY);
                }
                else if (y > 0){
                    deltaX = 0;
                    deltaY = -y;
                    postTranslate(deltaX, deltaY);
                }
            }
        }
        postInvalidate();
    }
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            mode = ZOOM;
            return  true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactor = detector.getScaleFactor();
            float focusX = detector.getFocusX();
            float focusY = detector.getFocusY();
            postScaleToImage(scaleFactor, focusX, focusY);
            return true;
        }
    }

    public PointF getCenter(){
        fillAbsoluteOffset();

        float x = (float) Math.abs(absoluteX) + viewWidth/2;
        float y = (float) Math.abs(absoluteY) + viewHeight/2;
        return new PointF(x, y);
    }

    protected void onViewClick (float xOnView, float yOnView){}

}
